/*

   This file is part of libmarco
   http://code.google.com/p/libmarco/

This module allows to create and manipulate matrices of bits.
The matrix is maintained as a vector of bits to minimize space wasted. (at most seven bits!).
The matrix is ZERO indexed 
i.e. m[1][1] is the second column, second row.
     m[0][0] is the first column, first row.
     m[0][3] in a 3x3 matrix is an invalid cell.

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "bitmatrix.h"

struct bit_matrix_t {
	unsigned cols;
	unsigned rows;
	char * bits;
};

unsigned get_num_rows(bit_matrix * m) {
	return m->rows;
}

unsigned get_num_cols(bit_matrix * m) {
	return m->cols;
}

bit_matrix * allocate_matrix(unsigned rows, unsigned cols) {
	
	bit_matrix * m = malloc(sizeof(bit_matrix));
	
	m->cols = cols;
	m->rows = rows;
	
	unsigned bytes = ((cols*rows)/8);
	if(((cols*rows) % 8) != 0) {
		bytes += 1;
	}
	
	m->bits =  malloc(bytes);
	memset(m->bits, '\0', bytes);
	
	return m;	
}

void destroy_matrix(bit_matrix * m) {
	assert(m != NULL);
	assert(m->bits != NULL);
	
	free(m->bits);
	free(m);
}

void set_bit(bit_matrix * m, unsigned row, unsigned col) {
	assert(row < m->rows);
	assert(col < m->cols);

	unsigned bit_number = (row*m->cols)+col;
	
	unsigned byte_offset = bit_number / 8;
	unsigned bit_offset = bit_number % 8;
	
	char byte = m->bits[byte_offset];
	byte |= (1<<bit_offset);
	
	m->bits[byte_offset] = byte;
}

void unset_bit(bit_matrix * m, unsigned row, unsigned col) {
	assert(row < m->rows);
	assert(col < m->cols);

	unsigned bit_number = (row*m->cols)+col;
	
	unsigned byte_offset = bit_number / 8;
	unsigned bit_offset = bit_number % 8;
	
	char byte = m->bits[byte_offset];
	byte &= (255 - (1<<bit_offset));
	
	m->bits[byte_offset] = byte;
}

bool is_bit_set(bit_matrix * m, unsigned row, unsigned col) {
	assert(row < m->rows);
	assert(col < m->cols);

	unsigned bit_number = (row*m->cols)+col;
	
	unsigned byte_offset = bit_number / 8;
	unsigned bit_offset = bit_number % 8;

	char byte = m->bits[byte_offset];
	char mask = 1<<bit_offset;
	
	// printf("BIT_NUM : %u\n", bit_number);
	// printf("BYTE_OFF: %u\n", byte_offset);
	// printf("BIT_OFF : %u\n", bit_offset);
	// printf("BYTE_VAL: %d\n", (int) byte);
	// printf("MASK_VAL: %d\n", (int) mask);
	// printf("(%d & %d) = %d\n", mask, byte, (mask & byte));
	if((mask & byte) == 0) {
		return false;
	} else {
		return true;
	}
}

static bool is_char_skippable(char c) {
	static char * skipchar_vector = MATRIX_PARSE_IGNORE_CHARACTERS;
	unsigned i;
	for(i = 0; i < MATRIX_PARSE_IGNORE_CHARACTERS_LENGTH; ++i) {
		if(c == skipchar_vector[i]) {
			return true;
		}
	}
	return false;
}

static bool is_char_invalid(char c){
	return (c != '1' && c != '0');
}

bool matrix_fill_from_file(bit_matrix * m, FILE * f) {
	char line_buffer[MATRIX_PARSE_LINEBUFFER_SIZE];
	char * retval;
	
	unsigned row;
	for(row = 0; row < get_num_rows(m); ++row) {
		retval = fgets(line_buffer, MATRIX_PARSE_LINEBUFFER_SIZE, f);

		if(retval != line_buffer) {
			printf("Error: Parsing matrix row:%u/%u.\n\t",
				row, get_num_rows(m));
			printf("Can't read next line\n");
			return false;
		}
		
		unsigned col;
		unsigned char_index = 0;
		unsigned max_char_index = strlen(line_buffer);
		
		for(col = 0; col < get_num_cols(m); ++col) {
			while(is_char_skippable(line_buffer[char_index]) && char_index < max_char_index) {
				char_index += 1;
			}
			
			if(char_index >= max_char_index) {
				printf("Error: Parsing matrix row:%u/%u, col:%u/%u.\n\t", 
					row, get_num_rows(m), col, get_num_cols(m));
				printf("This line contains less than %u valid columns\n", get_num_cols(m));
				return false;
			}
			
			char c = line_buffer[char_index];

			if(is_char_invalid(c)) {
				printf("Error: Parsing matrix row:%u/%u, col:%u/%u.\n\t", 
					row, get_num_rows(m), col, get_num_cols(m));
				printf("Found invalid character '%c' at offset %u\n", c, char_index);
				return false;
			}
			
			if(c == '1') {
				set_bit(m, row, col);
			} else if (c == '0') {
				unset_bit(m, row, col);
			}
			
			char_index += 1;
		}
	}
	return true;
}

void print_matrix(bit_matrix * m) {
	unsigned r, c;
	
	for(r = 0; r < get_num_rows(m); ++r) {
		for(c = 0; c < get_num_cols(m); ++c) {	
			if(is_bit_set(m, r, c)) {
				putchar('1');
			} else {
				putchar('0');
			}
			putchar(' ');
			
		}
		putchar('\n');
	}
}
